#pragma once
#include "config.hpp"
#include <pthread.h>
#include <unordered_map>

namespace cloud
{
    struct backupData
    {
        bool is_pack;         // 是否被压缩
        size_t size;          // 文件大小
        time_t atime;         // 最后一次访问时间
        time_t mtime;         // 最后一次修改时间
        std::string url_real; // 实际存储路径
        std::string url_pack; // 压缩包路径
        std::string url;      // 文件下载路径

        bool setBackupdata(const std::string &path)
        {
            FileTool ft(path);
            if (ft.exists() == false)
            {
                std::cerr << "data.hpp setBackupdata no file" << std::endl;
                return false;
            }

            config *c = config::getInstance();

            is_pack = false;
            size = ft.getSize();
            atime = ft.getATime();
            mtime = ft.getMTime();
            url_real = path;
            url_pack = c->getUrlPackdir() + ft.getFileName() + c->getSuffixPack();
            url = c->getUrlDownload() + ft.getFileName();

            return true;
        }
    };

    class dataManager
    {
    private:
        std::string _file_backup;
        pthread_rwlock_t _rwlock;
        std::unordered_map<std::string, backupData> _table;

    public:
        dataManager()
        {
            pthread_rwlock_init(&_rwlock, nullptr);
            _file_backup = config::getInstance()->getDataCloud();
            init();
        }

        ~dataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }

        void insert(const backupData &data)
        {
            pthread_rwlock_wrlock(&_rwlock);

            _table[data.url] = data;

            pthread_rwlock_unlock(&_rwlock);
            
            setFile();
        }

        void updata(const backupData &data)
        {
            pthread_rwlock_wrlock(&_rwlock);

            _table[data.url] = data;

            pthread_rwlock_unlock(&_rwlock);
            
            setFile();
        }

        bool getOneUrl(const std::string &url, backupData *data)
        {
            pthread_rwlock_wrlock(&_rwlock);

            auto pos = _table.find(url);
            if (pos == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *data = pos->second;

            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        bool getOneReal(const std::string &urlReal, backupData *data)
        {
            pthread_rwlock_wrlock(&_rwlock);

            auto pos = _table.begin();
            while (pos != _table.end())
            {
                if (pos->second.url_real == urlReal)
                {
                    *data = pos->second;

                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
                pos++;
            }

            pthread_rwlock_unlock(&_rwlock);
            return false;
        }

        void getAll(std::vector<backupData> *array)
        {
            pthread_rwlock_wrlock(&_rwlock);

            auto pos = _table.begin();
            while (pos != _table.end())
            {
                array->push_back(pos->second);
                pos++;
            }

            pthread_rwlock_unlock(&_rwlock);
        }

        void setFile()
        {
            std::vector<backupData> array;
            getAll(&array);

            Json::Value root;
            for (auto &it : array)
            {
                Json::Value tmp;
                tmp["is_pack"] = it.is_pack;
                tmp["size"] = it.size;
                tmp["atime"] = it.atime;
                tmp["mtime"] = it.mtime;
                tmp["url_real"] = it.url_real;
                tmp["url_pack"] = it.url_pack;
                tmp["url_url"] = it.url;
                root.append(tmp);
            }
            std::string content;
            jsonTool::serialize(root, &content);

            FileTool ft(_file_backup);
            ft.setContent(content);
        }

        void init()
        {
            FileTool ft(_file_backup);
            std::string content;
            ft.getAllContent(&content);

            Json::Value root;
            jsonTool::deserialize(content, &root);
            for (int i = 0; i < root.size(); i++)
            {
                backupData tmp;
                tmp.is_pack = root[i]["is_pack"].asBool();
                tmp.size = root[i]["size"].asInt64();
                tmp.atime = root[i]["atime"].asInt64();
                tmp.mtime = root[i]["mtime"].asInt64();
                tmp.url_real = root[i]["url_real"].asString();
                tmp.url_pack = root[i]["url_pack"].asString();
                tmp.url = root[i]["url_url"].asString();

                insert(tmp);
            }
        }
    };

}